<template>
  <el-dialog
    top="5vh"
    class="pure-search-dialog"
    v-model="show"
    :show-close="false"
    :width="app.isMobile ? '80vw' : '40vw'"
    :before-close="handleClose"
    :style="{
      borderRadius: '6px'
    }"
    append-to-body
    @opened="inputRef.focus()"
    @closed="inputRef.blur()"
  >
    <el-input
      ref="inputRef"
      size="large"
      v-model="keyword"
      clearable
      placeholder="搜索菜单"
      @input="handleSearch"
    >
      <template #prefix>
        <IconifyIconOffline
          :icon="Search"
          class="text-primary w-[24px] h-[24px]"
        />
      </template>
    </el-input>
    <div class="search-result-container">
      <el-scrollbar ref="scrollbarRef" max-height="calc(90vh - 140px)">
        <el-empty
          v-if="resultOptions.length === 0"
          description="暂无搜索结果"
        />
        <SearchResult
          v-else
          ref="resultRef"
          v-model:value="activePath"
          :options="resultOptions"
          @click="handleEnter"
        />
      </el-scrollbar>
    </div>
    <template #footer>
      <SearchFooter :total="resultOptions.length" />
    </template>
  </el-dialog>
</template>
<script setup lang="ts">
import { match } from "pinyin-pro";
import { useRouter } from "vue-router";
import SearchResult from "./SearchResult.vue";
import SearchFooter from "./SearchFooter.vue";
import { ref, computed, shallowRef } from "vue";
import { useDebounceFn, onKeyStroke } from "@vueuse/core";
import Search from "@iconify-icons/ri/search-line";
import { useAppHooks } from "@/core/pinia/store/app.store";
import { useMenuHooks } from "@/core/pinia/store/menu.store";
interface Props {
  /** 弹窗显隐 */
  value: boolean;
}

interface Emits {
  (e: "update:value", val: boolean): void;
}

const app = useAppHooks();
const menHooks = useMenuHooks();
const inputRef = ref<HTMLInputElement | null>(null);
const keyword = ref("");
const handleClose = () => {
  show.value = false;
  /** 延时处理防止用户看到某些操作 */
  setTimeout(() => {
    resultOptions.value = [];
    keyword.value = "";
  }, 200);
};
const handleSearch = useDebounceFn(() => {
  const flatMenusData = menHooks.flatMenus;
  resultOptions.value = flatMenusData.filter(menu =>
    keyword.value
      ? menu.text
          .toLocaleLowerCase()
          .includes(keyword.value.toLocaleLowerCase().trim()) ||
        match(
          menu.text.toLocaleLowerCase(),
          keyword.value.toLocaleLowerCase().trim()
        )
      : false
  );
  if (resultOptions.value?.length > 0) {
    activePath.value = resultOptions.value[0].link;
  } else {
    activePath.value = "";
  }
}, 300);

const emit = defineEmits<Emits>();
const props = withDefaults(defineProps<Props>(), {});
const router = useRouter();

const scrollbarRef = ref();
const resultRef = ref();
const activePath = ref("");

const resultOptions = shallowRef([]);

const show = computed({
  get() {
    return props.value;
  },
  set(val: boolean) {
    emit("update:value", val);
  }
});

const scrollTo = (index: number) => {
  const scrollTop = resultRef.value.handleScroll(index);
  scrollbarRef.value.setScrollTop(scrollTop);
};

/** key up */
const handleUp = () => {
  const { length } = resultOptions.value;
  if (length === 0) return;
  const index = resultOptions.value.findIndex(
    item => item.router === activePath.value
  );
  if (index === 0) {
    activePath.value = resultOptions.value[length - 1].router;
    scrollTo(resultOptions.value.length - 1);
  } else {
    activePath.value = resultOptions.value[index - 1].router;
    scrollTo(index - 1);
  }
};

/** key down */
const handleDown = () => {
  const { length } = resultOptions.value;
  if (length === 0) return;
  const index = resultOptions.value.findIndex(
    item => item.router === activePath.value
  );
  if (index + 1 === length) {
    activePath.value = resultOptions.value[0].router;
  } else {
    activePath.value = resultOptions.value[index + 1].router;
  }
  scrollTo(index + 1);
};

/** key enter */
const handleEnter = () => {
  const { length } = resultOptions.value;
  if (length === 0 || activePath.value === "") return;
  menHooks.setActivate(null, activePath.value);
  router.push(activePath.value);
  handleClose();
};

onKeyStroke("Enter", handleEnter);
onKeyStroke("ArrowUp", handleUp);
onKeyStroke("ArrowDown", handleDown);
</script>

<style lang="scss" scoped>
.search-result-container {
  margin-top: 12px;
}
</style>
